pub mod score_aggregator;
pub mod zscore;

use crate::db::FUDB;
use crate::model::indicators::boll::BOLL;
use crate::model::indicators::idc_score::IdcScoreConfig;
use crate::model::indicators::macd::MACD;
use crate::model::indicators::rsi::RSI;
use crate::model::indicators::IndicatorActionWrap;
use crate::model::kline_of::Kline;
use crate::model::FudaRecord;
use anyhow::anyhow;
use anyhow::Result;
use surrealdb::opt::PatchOp;
use surrealdb::sql::Id;
use surrealdb::sql::Thing;

/// Considering historical indicators over a period (like the last 5 days in your example)
/// can indeed add valuable context to your trading decisions, especially when individual signals might be noisy
/// or less reliable in isolation. By looking at the trend or consistency of signals over time,
/// you can potentially filter out short-term fluctuations and identify stronger trends or turning points.
/// Here's how you could integrate historical signals into your scoring system, specifically for MACD and BOLL:
/// 1. Maintain a Window of Historical Signals
/// For each indicator, maintain a rolling window of the past N indicators (e.g., the last 5 days' indicators).
pub struct IndicatorHistory<T: FinancialIndicator> {
    pub window_size: usize,
    pub indicators: Vec<T>,
    signals: Vec<Option<IndicatorActionWrap>>,
}

impl<T: FinancialIndicator> IndicatorHistory<T> {
    fn new(window_size: usize) -> Self {
        IndicatorHistory {
            window_size,
            indicators: Vec::with_capacity(window_size),
            signals: Vec::new(),
        }
    }

    fn push(&mut self, indicator: T) {
        if self.indicators.len() == self.window_size {
            self.indicators.remove(0); // Remove the oldest signal when window is full
        }
        self.indicators.push(indicator);

        // update signals dependent on the indicators
        self.signals.clear();
        for idc in self.indicators.iter() {
            if let Some(sigs) = idc.get_signals() {
                self.signals.extend_from_slice(&sigs);
            }
        }
    }

    fn signals(&self) -> &[Option<IndicatorActionWrap>] {
        &self.signals
    }
}

impl FinancialIndicator for MACD {
    fn get_signals(&self) -> Option<Vec<Option<IndicatorActionWrap>>> {
        let mut rs = Vec::new();
        rs.push(self.signal0.clone());
        rs.push(self.signal1.clone());

        Some(rs)
    }
}

impl FinancialIndicator for BOLL {
    fn get_signals(&self) -> Option<Vec<Option<IndicatorActionWrap>>> {
        let mut rs = Vec::new();
        rs.push(self.signal0.clone());

        Some(rs)
    }
}

impl FinancialIndicator for RSI {
    fn get_signals(&self) -> Option<Vec<Option<IndicatorActionWrap>>> {
        let mut rs = Vec::new();
        rs.push(self.signal0.clone());
        rs.push(self.signal1.clone());

        Some(rs)
    }
}
pub trait FinancialIndicator {
    // Define any common methods that make sense across all indicators.
    // For example, if all indicators can provide a signal in some form:
    fn get_signals(&self) -> Option<Vec<Option<IndicatorActionWrap>>>; // Or another appropriate method based on shared behavior
}

// Initialize histories for MACD and BOLL
//let mut macd_history = IndicatorHistory::new(5);
// let mut boll_history = IndicatorHistory::new(5);

/// 根据所有指标计算总分数和最终买卖建议
///
/// klt: Kline Type (分钟单位)
///
/// config: IdcSore设置
///
/// return 更新的记录数
///
pub async fn update_idc_score(
    mut klines: Vec<Kline>,
    config: Option<IdcScoreConfig>,
) -> Result<u32> {
    let _ = config;
    let mut updates = 0u32;

    zscore::zscore_macd_iterative(&mut klines);

    for (i, kline) in klines.iter().enumerate() {
        if let Some(Thing {
            id: Id::String(kid_str),
            ..
        }) = &kline.id
        {
            println!("macd[{i}] close={} ==> {:?}", kline.close, &kline.macd);

            let updated: Option<FudaRecord> = FUDB
                .update(("kline", kid_str))
                .patch(PatchOp::replace("/macd", &kline.macd))
                .await
                .map_err(|err| anyhow!(err))?;

            if let Some(_) = updated {
                updates += 1;
            }
        }
    }

    Ok(updates)
}

#[cfg(test)]
mod test {

    use crate::db;
    use crate::model::kline_of::Klt;
    use crate::model::security::Security;

    #[tokio::test]
    async fn test_update_macd() {
        let _ = db::init().await.unwrap();
    }
}
